#pragma once
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <unistd.h>
#include "sock.hpp"
// 缓冲区大小
#define NUM 1024
// 雷子数量
#define MINE_NUM 10
// 数组个数
#define ARR_NUM 10

class Mine_game
{
public:
    Mine_game(int num = ARR_NUM)
    {
        memset(_inbuffer, 0, NUM);
        _listensock = _sock.Creat();
        _sock.Bind();
        _sock.Listen();
        // 随机数种子
        srand((unsigned int)time(nullptr));
        _mine_arr.resize(num + 1);
        for (auto &arr : _mine_arr)
            arr.resize(num + 1, 0);
        _show_arr.resize(num + 1);
        for (auto &arr : _show_arr)
            arr.resize(num + 1, '#');
        for (size_t i = 0; i < _show_arr.size(); i++)
        {
            for (size_t j = 0; j < _show_arr[i].size(); j++)
            {
                if (i < 1)
                    _show_arr[i][j] = j;
                else
                    _show_arr[i][0] = i;
            }
        }
        for (size_t i = 0; i < _mine_arr.size(); i++)
        {
            for (size_t j = 0; j < _mine_arr[i].size(); j++)
            {
                if (i < 1)
                    _mine_arr[i][j] = j;
                else
                    _mine_arr[i][0] = i;
            }
        }
    }

    void Start()
    {
        int sock = _sock.Accept();
        int input = 0;
        do
        {
            Init();
            _buffer += "Please choose\n\r";
            Menu(sock);
            ssize_t s = recv(sock, _inbuffer, NUM - 1, 0);
            _inbuffer[s] = '\0';
            input = _inbuffer[0] - '0';
            switch (input)
            {
            case 1:
                Play(sock);
                break;
            case 2:
                _buffer += "exit game\n\r";
                send(sock, _buffer.c_str(), _buffer.size(), 0);
                _buffer.clear();
                break;
            default:
                _buffer += "This option is not available, please select it again\n\r";
                send(sock, _buffer.c_str(), _buffer.size(), 0);
                _buffer.clear();
                break;
            }
        } while (input != 2);
    }

private:
    void Init()
    {
        for (size_t i = 1; i < _show_arr.size(); i++)
        {
            for (size_t j = 1; j < _show_arr[i].size(); j++)
            {
                _show_arr[i][j] = '#';
            }
        }
        for (size_t i = 1; i < _mine_arr.size(); i++)
        {
            for (size_t j = 1; j < _mine_arr[i].size(); j++)
            {
                _mine_arr[i][j] = 0;
            }
        }
        Layout();
    }
    void Around_Mine(int x, int y)
    {
        while (x - 1 >= ARR_NUM || x <= 0)
            return;
        while (y - 1 >= ARR_NUM || y <= 0)
            return;
        while (_show_arr[x][y] != '#')
            return;
        if (x == 1 && y == 1)
            _show_arr[x][y] = (_mine_arr[x + 1][y] + _mine_arr[x + 1][y + 1] + _mine_arr[x][y + 1]) / '*' + '0';
        else if ((x == 1 && y > 1) && y < ARR_NUM)
            _show_arr[x][y] = (_mine_arr[x + 1][y] + _mine_arr[x + 1][y - 1] + _mine_arr[x + 1][y + 1] + _mine_arr[x][y - 1] + _mine_arr[x][y + 1]) / '*' + '0';
        else if (x == 1 && y == ARR_NUM)
            _show_arr[x][y] = (_mine_arr[x][y - 1] + _mine_arr[x + 1][y - 1] + _mine_arr[x + 1][y]) / '*' + '0';
        else if ((x > 1 && y == 1) && x < ARR_NUM)
            _show_arr[x][y] = (_mine_arr[x - 1][y] + _mine_arr[x + 1][y] + _mine_arr[x][y + 1] + _mine_arr[x - 1][y + 1] + _mine_arr[x + 1][y + 1]) / '*' + '0';
        else if (x == ARR_NUM && y == 1)
            _show_arr[x][y] = (_mine_arr[x - 1][y] + _mine_arr[x - 1][y + 1] + _mine_arr[x][y + 1]) / '*' + '0';
        else if ((x == ARR_NUM && y > 1) && y < ARR_NUM)
            _show_arr[x][y] = (_mine_arr[x][y - 1] + _mine_arr[x][y + 1] + _mine_arr[x - 1][y] + _mine_arr[x - 1][y - 1] + _mine_arr[x - 1][y + 1]) / '*' + '0';
        else if (x == ARR_NUM && y == ARR_NUM)
            _show_arr[x][y] = (_mine_arr[x][y - 1] + _mine_arr[x - 1][y] + _mine_arr[x - 1][y - 1]) / '*' + '0';
        else if ((x > 1 && x < ARR_NUM) && y == ARR_NUM)
            _show_arr[x][y] = (_mine_arr[x - 1][y] + _mine_arr[x + 1][y] + _mine_arr[x][y - 1] + _mine_arr[x - 1][y - 1] + _mine_arr[x + 1][y - 1]) / '*' + '0';
        else
            _show_arr[x][y] = (_mine_arr[x - 1][y - 1] + _mine_arr[x - 1][y] + _mine_arr[x - 1][y + 1] + _mine_arr[x][y - 1] +
                               _mine_arr[x][y + 1] + _mine_arr[x + 1][y - 1] + _mine_arr[x + 1][y] + _mine_arr[x + 1][y + 1]) /
                                  '*' +
                              '0';
        if (_show_arr[x][y] == '0')
        {
            Around_Mine(x - 1, y - 1);
            Around_Mine(x - 1, y);
            Around_Mine(x - 1, y + 1);
            Around_Mine(x, y - 1);
            Around_Mine(x, y + 1);
            Around_Mine(x + 1, y - 1);
            Around_Mine(x + 1, y);
            Around_Mine(x + 1, y + 1);
            // if (x == 1 && y == 1)
            // {
            //     Around_Mine(x + 1, y);
            //     Around_Mine(x + 1, y + 1);
            //     Around_Mine(x, y + 1);
            // }
            // else if ((x == 1 && y > 1) && y < ARR_NUM)
            // {
            //     Around_Mine(x + 1, y);
            //     Around_Mine(x + 1, y + 1);
            //     Around_Mine(x + 1, y - 1);
            //     Around_Mine(x, y - 1);
            //     Around_Mine(x, y + 1);
            // }
            // else if (x == 1 && y == ARR_NUM)
            // {
            //     Around_Mine(x, y - 1);
            //     Around_Mine(x + 1, y - 1);
            //     Around_Mine(x + 1, y);
            // }
            // else if ((x > 1 && y == 1) && x < ARR_NUM)
            // {
            //     Around_Mine(x - 1, y);
            //     Around_Mine(x + 1, y);
            //     Around_Mine(x, y + 1);
            //     Around_Mine(x - 1, y + 1);
            //     Around_Mine(x + 1, y + 1);
            // }
            // else if (x == ARR_NUM && y == 1)
            // {
            //     Around_Mine(x - 1, y);
            //     Around_Mine(x - 1, y + 1);
            //     Around_Mine(x, y + 1);
            // }
            // else if ((x == ARR_NUM && y > 1) && y < ARR_NUM)
            // {
            //     Around_Mine(x, y - 1);
            //     Around_Mine(x, y + 1);
            //     Around_Mine(x - 1, y);
            //     Around_Mine(x - 1, y - 1);
            //     Around_Mine(x - 1, y + 1);
            // }
            // else if (x == ARR_NUM && y == ARR_NUM)
            // {
            //     Around_Mine(x, y - 1);
            //     Around_Mine(x - 1, y);
            //     Around_Mine(x - 1, y - 1);
            // }
            // else if ((x > 1 && x < ARR_NUM) && y == ARR_NUM)
            // {
            //     Around_Mine(x - 1, y);
            //     Around_Mine(x + 1, y);
            //     Around_Mine(x, y - 1);
            //     Around_Mine(x - 1, y - 1);
            //     Around_Mine(x + 1, y - 1);
            // }
        }
        else
            return;
    }
    bool Tag(int sock)
    {
        int count = 0;
        char tag = 0;
        _buffer += "Whether or not to mark y/n?\n\r";
        send(sock, _buffer.c_str(), _buffer.size(), 0);
        _buffer.clear();
        recv(sock, _inbuffer, NUM - 1, 0);
        tag = _inbuffer[0];
        memset(_inbuffer, 0, NUM);
        if (tag == 'y')
        {
            int x;
            int y;
            if (_mine > 0)
            {
                _buffer += "please choose flag x:\n\r";
                send(sock, _buffer.c_str(), _buffer.size(), 0);
                _buffer.clear();

                recv(sock, _inbuffer, NUM - 1, 0);
                x = atoi(_inbuffer);
                memset(_inbuffer, 0, NUM);

                _buffer += "please choose flag y:\n\r";
                send(sock, _buffer.c_str(), _buffer.size(), 0);
                _buffer.clear();

                recv(sock, _inbuffer, NUM - 1, 0);
                y = atoi(_inbuffer);
                memset(_inbuffer, 0, NUM);

                _tag.push_back(std::make_pair(x, y));
            }
            else
            {
                _buffer += "The marker runs out\n\r";
                send(sock, _buffer.c_str(), _buffer.size(), 0);
                _buffer.clear();
            }
        }
        if (_tag.size() == 10)
        {
            for (const auto &e : _tag)
            {
                if (_show_arr[e.first][e.second] == '*')
                    count++;
            }
        }
        else
            return false;
        if (count == 10)
        {
            _buffer += "you are winer\n\r";
            send(sock, _buffer.c_str(), _buffer.size(), 0);
            _buffer.clear();
            return true;
        }
        else
            return false;
    }
    void Play(int sock)
    {
        int x;
        int y;
        while (true)
        {
            Print_Show(sock);
            // 序列化以及反序列化
            _buffer += "please choose x:\n\r";
            send(sock, _buffer.c_str(), _buffer.size(), 0);
            _buffer.clear();
            recv(sock, _inbuffer, NUM - 1, 0);
            x = atoi(_inbuffer);
            memset(_inbuffer, 0, NUM);
            _buffer += "please choose y:\n\r";
            send(sock, _buffer.c_str(), _buffer.size(), 0);
            _buffer.clear();
            recv(sock, _inbuffer, NUM - 1, 0);
            y = atoi(_inbuffer);
            memset(_inbuffer, 0, NUM);
            if ((x <= 0 || x - 1 > ARR_NUM) || y <= 0 || y - 1 > ARR_NUM)
            {
                _buffer += "coordinate not true,please choose again\n\r";
                send(sock, _buffer.c_str(), _buffer.size(), 0);
                _buffer.clear();
                continue;
            }
            if (_mine_arr[x][y] == '*')
            {
                _buffer += "sorry, you defeat\n\r";
                _buffer += "print mine\n";
                send(sock, _buffer.c_str(), _buffer.size(), 0);
                _buffer.clear();
                Print_Mine(sock);
                break;
            }
            if (_mine_arr[x][y] != '*' && _show_arr[x][y] == '#')
            {
                Around_Mine(x, y);
                int ret2 = Tag(sock);
                if (ret2)
                {
                    _buffer += "you are winer\n\r";
                    send(sock, _buffer.c_str(), _buffer.size(), 0);
                    _buffer.clear();
                    break;
                }
                int ret = Winer(sock);
                if (ret)
                {
                    _buffer += "you are winer\n\r";
                    send(sock, _buffer.c_str(), _buffer.size(), 0);
                    _buffer.clear();
                    break;
                }
            }
            else if (_mine_arr[x][y] != '*' && _show_arr[x][y] != '#')
            {
                _buffer += "please choose again\n\r";
                send(sock, _buffer.c_str(), _buffer.size(), 0);
                _buffer.clear();
                continue;
            }
        }
    }
    void Layout()
    {
        int count = MINE_NUM;
        while (count > 0)
        {
            _x = rand() % ARR_NUM;
            _y = rand() % ARR_NUM;
            if (_mine_arr[_x + 1][_y + 1] != '*')
                _mine_arr[_x + 1][_y + 1] = '*';
            else
                continue;
            count--;
        }
    }
    void Print_Show(int sock)
    {
        _buffer += "-------show-------\n\r";
        for (size_t i = 0; i < _show_arr.size(); i++)
        {
            for (size_t j = 0; j < _show_arr.size(); j++)
            {
                if (i < 1)
                    _buffer += std::to_string(_show_arr[i][j]);
                else if (i >= 1 && j == 0)
                    _buffer += std::to_string(_show_arr[i][0]);
                else
                    _buffer += _show_arr[i][j];
                _buffer += " ";
            }
            _buffer += "\n\r";
        }
        send(sock, _buffer.c_str(), _buffer.size(), 0);
        _buffer.clear();
    }
    void Print_Mine(int sock)
    {
        _buffer += "-------mine-------\n\r";
        for (size_t i = 0; i < _mine_arr.size(); i++)
        {
            for (size_t j = 0; j < _mine_arr.size(); j++)
            {
                if (i < 1)
                    _buffer += std::to_string(_mine_arr[i][j]);
                else if (i >= 1 && j == 0)
                    _buffer += std::to_string(_mine_arr[i][0]);
                else
                {
                    if (_mine_arr[i][j] == 0)
                        _buffer += " ";
                    else
                        _buffer += _mine_arr[i][j];
                }
                _buffer += " ";
            }
            _buffer += "\n\r";
        }
        send(sock, _buffer.c_str(), _buffer.size(), 0);
        _buffer.clear();
    }
    void Menu(int sock)
    {
        _buffer += "----------------------------\n\r";
        _buffer += "----------please choose-----\n\r";
        _buffer += "----------1. play-----------\n\r";
        _buffer += "----------2. exit-----------\n\r";
        _buffer += "----------------------------\n\r";
        send(sock, _buffer.c_str(), _buffer.size(), 0);
        _buffer.clear();
    }
    bool Winer(int sock)
    {
        int count = 0;
        for (const auto &arr : _show_arr)
        {
            for (const auto &e : arr)
            {
                if (e == '#')
                    count++;
            }
        }
        if (count == 10)
        {
            _buffer += "you are winer\n\r";
            send(sock, _buffer.c_str(), _buffer.size(), 0);
            _buffer.clear();
            return true;
        }
        else
            return false;
    }

public:
    // 读取缓冲区
    char _inbuffer[NUM];
    // 发送缓冲区
    std::string _buffer;
    // 缓冲区
    char _bufe[NUM];

private:
    // 监听套接字
    int _listensock;
    // 雷子数量
    int _mine = MINE_NUM;
    // 横坐标
    int _x;
    // 纵坐标
    int _y;
    // 网络套接字
    Sock _sock;
    // 标记数组
    std::vector<std::pair<int, int>> _tag;
    // 排雷数组
    std::vector<std::vector<char>> _mine_arr;
    // 显示数组
    std::vector<std::vector<char>> _show_arr;
};